import * as path from 'path';

import { IncrementalExecTask } from '../../../../src/base/external/task/incremental-exec-task.js';
import {
  Input,
  InputFile,
  InputFiles,
  Inputs,
  OutputFile,
  OutputFiles,
  IgnoreSuperIncremental
} from '../../../../src/base/incremental-decorator/index.js';
import { wrapIncrementalDeclarations } from '../../../../src/base/incremental-decorator/wrap.js';
import { FileSet } from '../../../../src/base/internal/snapshot/util/file-set.js';
import { TaskInputValue } from '../../../../src/base/internal/snapshot/util/task-input-value-entry.js';
import { isIncrementalTask } from '../../../../src/base/util/task-util.js';
import { hash } from '@ohos/hvigor-common';


class OhosHap extends IncrementalExecTask {
  protected customTargetService = {
    buildMode: 'test'
  };

  @Input()
  protected customTargetName: string | undefined;
  @Input()
  protected get customTaskName() {
    return 'OhosHap';
  }
  @Input()
  protected get customBuildOptions() {
    return { a: 1 };
  }
  @Inputs()
  protected get customPkgName2PkgVersion () {
    return new Map<string, number>()
      .set('json5', 0)
      .set('axios', 1);
  }

  customCaseSensitive = true;
  customDirname = __dirname;
  customFilename = __filename;
  declareInputs(): Map<string, TaskInputValue> {
    return super.declareInputs()
      .set('customCaseSensitive', this.customCaseSensitive);
  }
  declareInputFiles(): FileSet {
    return super.declareInputFiles().addEntry(this.customDirname, { isDirectory: true });
  }
  declareOutputFiles(): FileSet {
    return super.declareOutputFiles().addEntry(this.customFilename);
  }
}

class OhosModule extends OhosHap {
  @InputFile()
  protected customJsonPath = path.join(__dirname, 'input-file.json');
  @InputFiles()
  protected customJsonPaths = [__filename, 'abc.json'];
  @OutputFile()
  protected customOutputFile = path.join(__dirname, 'output-file.json');
  @OutputFiles({ isDirectory: true })
  protected customOutputFiles = [__dirname];
  @Input()
  protected customData1 = [1, 2];
  @Input()
  protected customData2 = ['1', '2'];
  @Input()
  protected customData3 = [true, false];
  @Input()
  protected customData4 = [{ a: 1 }, false];
}

class DecoratorTest extends OhosModule {
  @Input()
  protected get customTaskName() {
    return 'DecoratorTest';
  }
  @Input()
  protected get customBuildMode() {
    return this.customTargetService.buildMode;
  }

  constructor() {
    super(
      {
        getName() {
          return 'DecoratorTest';
        }
      } as any,
      'DecoratorTest'
    );
  }
}

class NoDecoratorTest extends IncrementalExecTask {
  constructor() {
    super(
      {
        getName() {
          return 'NoDecoratorTest';
        }
      } as any,
      'NoDecoratorTest'
    );
  }
}

class OneDecoratorTest extends IncrementalExecTask {
  @Input()
  protected get customTaskName() {
    return 'OneDecoratorTest';
  }
  constructor() {
    super(
      {
        getName() {
          return 'OneDecoratorTest';
        }
      } as any,
      'OneDecoratorTest'
    );
  }
}

class StopInputsDecoratorTest extends OhosModule {
  @Input()
  protected get customTaskName() {
    return 'StopInputsDecoratorTest';
  }
  @Input()
  protected get customBuildMode() {
    return this.customTargetService.buildMode;
  }

  @IgnoreSuperIncremental()
  declareInputs(): Map<string, TaskInputValue> {
    return new Map();
  }
}

class StopInputFilesDecoratorTest extends StopInputsDecoratorTest {
  @InputFile()
  private testInputFile: string = __filename;
  @IgnoreSuperIncremental()
  declareInputFiles(): FileSet {
    return new FileSet();
  }
}

class StopOutputFilesDecoratorTest extends StopInputFilesDecoratorTest {
  @OutputFile()
  private testOutputFile: string = './a.json';
  @IgnoreSuperIncremental()
  declareOutputFiles(): FileSet {
    return new FileSet();
  }

  constructor() {
    super(
      {
        getName() {
          return 'StopOutputFilesDecoratorTest';
        }
      } as any,
      'StopOutputFilesDecoratorTest'
    );
  }
}


describe('test incremental decorator', () => {
  test('no decorator test', () => {
    const task1 = new NoDecoratorTest();
    const oldDeclareInputs = task1.declareInputs;
    const oldDeclareInputFiles = task1.declareInputFiles;
    const oldDeclareOutputFiles = task1.declareOutputFiles;
    wrapIncrementalDeclarations(task1);

    // 没有装饰器的类，不需要重设
    expect(task1.declareInputs === oldDeclareInputs).toEqual(true);
    expect(task1.declareInputFiles === oldDeclareInputFiles).toEqual(true);
    expect(task1.declareOutputFiles === oldDeclareOutputFiles).toEqual(true);
  });

  test('one decorator test', () => {
    const task2 = new OneDecoratorTest();
    wrapIncrementalDeclarations(task2);

    // 只要有装饰器，都认为是增量任务
    expect(isIncrementalTask(task2)).toEqual(true);
  });

  test('stop decorator test', () => {
    const task2 = new StopOutputFilesDecoratorTest();
    wrapIncrementalDeclarations(task2);
    expect(task2.declareInputs().size).toEqual(2);
    expect(task2.declareInputFiles().collect().size).toEqual(1);
    expect(task2.declareOutputFiles().collect().size).toEqual(1);
  });

  test('decorator basic usage', () => {
    const instance = new DecoratorTest();
    wrapIncrementalDeclarations(instance);
    const inputsMap = instance.declareInputs();
    const inputFileMap = instance.declareInputFiles().collect();
    const outputFileMap = instance.declareOutputFiles().collect();

    // 保持原有的declareInputs的值
    expect(inputsMap.get('customCaseSensitive')).toEqual(true);

    // 保持原有的declareInputFiles的值
    expect(inputFileMap.has(__dirname)).toEqual(true);

    // 保持原有的declareOutputFiles的值
    expect(outputFileMap.has(__filename)).toEqual(true);

    // @Input: undefined | null 会转换为空字符串
    expect(inputsMap.get('customTargetName')).toEqual('');

    // @Input: 值为对象，会被stringify，同时hash
    expect(inputsMap.get('customBuildOptions')).toEqual(hash(JSON.stringify((instance as any).customBuildOptions)));

    // @Inputs: 值为Map，逐个加入
    expect(inputsMap.get('customPkgName2PkgVersion-json5')).toEqual(0);
    expect(inputsMap.get('customPkgName2PkgVersion-axios')).toEqual(1);

    // @Input: 值为string | number | boolean数组时，原封不动加入
    expect((inputsMap.get('customData1') as Array<number>).join(',')).toEqual((instance as any).customData1.join(','));
    expect((inputsMap.get('customData2') as Array<string>).join(',')).toEqual((instance as any).customData2.join(','));
    expect((inputsMap.get('customData3') as Array<boolean>).join(',')).toEqual((instance as any).customData3.join(','));

    // @Input: 值不是 string | number | boolean数组时，值stringify再hash
    expect(inputsMap.get('customData4')).toEqual(hash(JSON.stringify((instance as any).customData4)));

    // @InputFile: 文件不存在时，不加入
    expect(inputFileMap.has((instance as any).customJsonPath)).toEqual(false);

    // @InputFiles: 文件不存在时，不加入
    expect(inputFileMap.has(__filename)).toEqual(true);
    expect(inputFileMap.has('abc.json')).toEqual(false);

    // @OutputFile: 不需要判断文件是否存在
    expect(outputFileMap.has((instance as any).customOutputFile)).toEqual(true);
    expect(outputFileMap.has(__dirname)).toEqual(true);

    expect(inputsMap.get('customBuildMode')).toEqual((instance as any).customBuildMode);

    // 同名属性，子类覆盖父类
    expect(inputsMap.get('customTaskName')).toEqual('DecoratorTest');
  });
});
